import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FormBottonsConfigService } from '../services';
import { NzSafeAny } from 'ng-zorro-antd/core/types';
import { DocumentStatus, EditState } from './contracts';
import { DocumentStateService } from './document-detail.state.service';
import snq from 'snq';
import { ConfigStateService } from '@abp/ng.core';

@Component({
  // eslint-disable-next-line @angular-eslint/component-selector
  selector: '',
  template: ``,
})
export abstract class DocumentDetailComponent<
  T extends { id: string; documentStatus: number; lines: NzSafeAny[] }
> implements OnInit
{
  public validateForm: FormGroup;
  public validateFormLines: NzSafeAny[] = [];
  searchText: string;
  stepLength: number;

  get currentModel(): T {
    return this.stateService.currentModel;
  }

  get formReadonly(): boolean {
    if (!this.currentModel?.id) return false;
    if (this.currentModel.documentStatus == DocumentStatus.Open) return false;

    return true;
  }

  get isLinesModified(): boolean {
    if (!this.currentModel?.lines?.length && !this.validateFormLines?.length) return false;
    if (this.currentModel?.lines?.length !== this.validateFormLines?.length) return true;
    for (var formLine of this.validateFormLines) {
      if (!formLine.id) return true;
      var line = this.currentModel.lines.find(x => x.id == formLine.id);
      if (!line) return true;
      for (const [key, val] of Object.entries(formLine)) {
        if (!line[key]) continue;
        if (line[key] != val) return true;
      }
    }
    // if (this.currentModel.lines.some(x => this.validateFormLines.every(y => y.id != x.id)))
    //   return true;
    // if (this.validateFormLines.some(x => this.currentModel.lines.every(y => y.id != x.id)))
    //   return true;
  }

  constructor(
    public fb: FormBuilder,
    public buttonConfigService: FormBottonsConfigService,
    public stateService: DocumentStateService<T>,
    public config: ConfigStateService
  ) {
    this.stepLength = Number(this.config.getSetting('BasicArchives.Storages.StepLength'));
  }

  ngOnInit(): void {
    this.initValidateForm();
    this.validateForm.valueChanges.subscribe(data => this.onFormValueChanged(data));

    this.buttonConfigService.pageButtonGroup.visable = false;
    this.buttonConfigService.outputButtonGroup.visable = false;

    this.buttonConfigService.onSaveClick.subscribe(() => this.save());
    this.buttonConfigService.onGiveupClick.subscribe(() => this.giveUp());
    this.buttonConfigService.onNewClick.subscribe(() => this.changeToEmpty());
    this.buttonConfigService.onCopyClick.subscribe(() => this.copyToCreating());
    this.buttonConfigService.onDeleteClick.subscribe(() => this.delete());

    this.buttonConfigService.onSubmitClick.subscribe(() => this.submit());
    this.buttonConfigService.onBackClick.subscribe(() => this.back());
    this.buttonConfigService.onApproveClick.subscribe(() => this.approve());
    this.buttonConfigService.onUnapproveClick.subscribe(() => this.unapprove());

    this.buttonConfigService.findButton.setVisable = () =>
      (this.buttonConfigService.findButton.visable = false);
    this.buttonConfigService.onListClick.subscribe(() => this.navigateToList());

    this.stateService.onStateChanged.subscribe(editState => {
      this.buttonConfigService.changeState(editState);
    });

    this.stateService.onCurrentModelChanged.subscribe(model => {
      if (this.stateService.state == EditState.Empty) {
        this.resetFormValue();
      } else if (
        this.stateService.state == EditState.View ||
        this.stateService.state == EditState.Appoving ||
        this.stateService.state == EditState.Appoved
      ) {
        this.setFormValue(model);
      }
    });

    this.buttonConfigService.saveButton.setEnabled = () => {
      if (
        this.buttonConfigService.editState == EditState.Creating ||
        this.buttonConfigService.editState == EditState.Editing
      ) {
        if (this.validateForm.valid && this.validateFormLines?.length)
          this.buttonConfigService.saveButton.enabled = true;
        else this.buttonConfigService.saveButton.enabled = false;
      } else {
        this.buttonConfigService.saveButton.enabled = false;
      }
    };

    this.afterInit();
  }

  public afterInit() {}

  public giveUp() {
    if (this.stateService.state == EditState.Creating) {
      this.stateService.giveupToEmpty();
    } else if (this.stateService.state == EditState.Editing) {
      this.stateService.giveupToView();
    }
  }

  public changeToEmpty() {
    this.stateService.changeToEmpty();
  }

  public copyToCreating() {
    this.stateService.copyToCreating().then(model => this.setFormValue(model));
  }

  public delete() {
    this.stateService.delete().then(() => this.resetFormValue());
  }

  public submit() {
    this.stateService.changeStatus(DocumentStatus.Waiting);
  }

  public back() {
    this.stateService.changeStatus(DocumentStatus.Open);
  }

  public approve() {
    this.stateService.changeStatus(DocumentStatus.Approved);
  }

  public unapprove() {
    this.stateService.changeStatus(DocumentStatus.Open);
  }

  public select() {
    this.refSelectModels().then(models => {
      if (!!models?.length) {
        this.stateService.changeToView(models[0].id);
      }
    });
  }

  public search() {
    if (!this.searchText) return;
    this.stateService
      .search(this.searchText)
      .then(model => {
        this.stateService.changeToView(model.id);
      })
      .catch(() => {});
  }

  public navigateToList() {
    this.stateService.navigateToList();
  }

  public onFormValueChanged(data: any) {
    switch (this.stateService.state) {
      case EditState.Empty:
      case EditState.Creating:
        for (const [key, val] of Object.entries(data)) {
          if (!val) continue;
          this.stateService.changeToCreating();
          this.buttonConfigService.changeState(EditState.Creating);
          break;
        }
        break;

      case EditState.View:
      case EditState.Editing:
        for (const [key, val] of Object.entries(data)) {
          if (!this.stateService.currentModel[key]) continue;
          if (!val && !this.stateService.currentModel[key]) continue;
          if (val == this.stateService.currentModel[key]) continue;
          this.stateService.changeToEditing();
          this.buttonConfigService.changeState(EditState.Editing);
          break;
        }
        break;
      default:
        break;
    }
  }

  public save() {
    this.stateService.controlValidate(!this.validateForm.valid);
    if (this.stateService.state == EditState.Creating) {
      this.stateService.save({
        ...this.validateForm.value,
        lines: this.validateFormLines,
        ...this.getEntity(),
      });
    } else if (this.stateService.state == EditState.Editing) {
      this.stateService.edit({
        ...this.stateService.currentModel,
        ...this.validateForm.value,
        lines: this.validateFormLines,
        id: this.stateService.currentModel.id,
        ...this.getEntity(),
      });
    }
  }

  protected getEntity() {
    return {};
  }

  deleteLine(line) {
    this.validateFormLines.splice(this.validateFormLines.indexOf(line), 1);
    this.onFormLineChanged();
  }

  public addLine() {
    var maxLineNo = 0;
    if (this.validateFormLines.length) {
      maxLineNo = snq(() =>
        this.validateFormLines.map(x => x.lineNo)?.reduce((a, b) => (a > b ? a : b))
      );
    }

    this.refEditLine({ lineNo: maxLineNo + this.stepLength }).then(backModel => {
      if (!!backModel) {
        this.validateFormLines = [...this.validateFormLines, backModel];
        this.onFormLineChanged();
      }
    });
  }

  onFormLineChanged() {
    switch (this.buttonConfigService.editState) {
      case EditState.Empty:
        if (this.isLinesModified) {
          this.stateService.changeToCreating();
          this.buttonConfigService.changeState(EditState.Creating);
        }
        break;
      case EditState.Creating:
        this.stateService.changeToCreating();
        this.buttonConfigService.changeState(EditState.Creating);
        break;
      case EditState.View:
        if (this.isLinesModified) {
          this.stateService.changeToEditing();
          this.buttonConfigService.changeState(EditState.Editing);
        }
        break;
      case EditState.Editing:
        this.stateService.changeToEditing();
        this.buttonConfigService.changeState(EditState.Editing);
        break;
      default:
        break;
    }
  }

  public resetFormValue() {
    this.validateForm?.reset({}, { emitEvent: false });
    this.validateFormLines = [];
    this.updateFormValidity();
  }

  public setFormValue(model: T) {
    this.validateForm.patchValue(model, { emitEvent: false });
    this.validateFormLines = [...model.lines];
    this.updateFormValidity();
  }

  protected updateFormValidity() {
    for (const key in this.validateForm.controls) {
      this.validateForm.controls[key].markAsPristine();
      this.validateForm.controls[key].markAsUntouched();
      this.validateForm.controls[key].updateValueAndValidity();
    }
  }

  public abstract initValidateForm();
  public abstract refSelectModels(): Promise<T[]>;
  public abstract refEditLine(model: NzSafeAny): Promise<NzSafeAny>;
}
